package cn.xnatural.enet.event;

import org.slf4j.helpers.MessageFormatter;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;

public abstract class Listener {
    /**
     * 排序. 一个事件对应多个监听器时生效. {@link EP#resolve(Object)} {@link EP#fire(EC)}
     */
    protected float         order;
    /**
     * 是否异步
     */
    protected boolean       async;
    /**
     * 执行限制次数. <=0 不限制
     */
    protected int           limit;
    /**
     * 执行次数统计
     */
    protected AtomicInteger count;


    /**
     * 设置执行次数限制
     * @param limit 限制
     */
    protected Listener limit(int limit) {
        if (limit > 0) {
            this.limit = limit;
            count = new AtomicInteger();
        }
        return this;
    }


    /**
     * 同一事件中, 为不同的监听器制定不同的参数
     */
    protected Object[] args(EC ec) { return ec.args; }


    /**
     * 执行完成调用
     */
    protected void pass(EC ec, Throwable ex, Object r, long spend) {
        resultAcceptor(ec).accept(r, ex);
        if (ex == null) {
            ec.passed(this, true);
            if (ec.track) {
                ec.ep().log.info("ENET({},{}): success passed {}, result: {}, spend: {}ms, args: {}", ec.eName, ec.id(), execId(), r, spend, Arrays.toString(args(ec)));
            }
        } else {
            ec.passed(this, false);
            ec.ep().log.error(MessageFormatter.arrayFormat(
                    "ENET({},{}): fail passed {}, result: {}, spend: {}ms, args: {}",
                    new Object[]{ec.eName, ec.id(), execId(), r, spend, Arrays.toString(args(ec))}
            ).getMessage(), ex);
        }
        if (count != null && count.get() >= limit) {
            List<Listener> ls = ec.ep().lsMap.get(ec.eName);
            if (ls == null) ec.willPass = null; //当执行次数达到limit, 后面的触发到这后, 让listener为空
            else {
                ls.remove(this);
                if (ls.isEmpty()) ec.ep().lsMap.remove(ec.eName);
            }
        }
        ec.tryFinish();
    }


    /**
     * 监听器执行结果处理器
     */
    protected <T> BiConsumer<T, Throwable> resultAcceptor(EC ec) { return (r, ex) -> ec.addResult(this, ex, r); }


    // 调用此监听器
    protected void invoke(EC ec) {
        long start = System.currentTimeMillis();
        try {
            if (tryLimited()) return;
            AtomicBoolean flag = new AtomicBoolean(false);
            doInvoke(ec, (r, ex) -> { // 只允许调一次
                if (flag.compareAndSet(false, true)) {
                    pass(ec, ex, r, System.currentTimeMillis() - start);
                }
            });
        } catch (Throwable ex) {
            pass(ec, ex, null, System.currentTimeMillis() - start);
        }
    }


    /**
     * 尝试被限制
     * @return true: 被限制, false: 没被限制(可继续执行)
     */
    protected boolean tryLimited() {
        if (count == null) return false;
        int c = count.get();
        if (c >= limit) return true;
        if (count.compareAndSet(c, c + 1)) return false;
        return tryLimited();
    }


    /**
     * 实际调用执行
     * @param resultAcceptor 第一个参数接收执行结果, 第二个参数接收执行异常
     */
    abstract protected void doInvoke(EC ec, BiConsumer<Object, Throwable> resultAcceptor) throws Throwable;


    /**
     * 执行id, 表示监听器此次执行id
      */
    abstract public String execId();


    public static class MethodListener extends Listener {
        //  监听执行体. (一个方法).
        protected final Object source;
        protected final Method m;

        public MethodListener(Object source, Method m) {
            if (source == null) throw new IllegalArgumentException("MethodListener source required");
            if (m == null) throw new IllegalArgumentException("MethodListener method required");
            this.source = source;
            this.m = m;
        }

        @Override
        protected void doInvoke(EC ec, BiConsumer<Object, Throwable> resultAcceptor) throws Throwable {
            Object r = null;
            if (m.getParameterCount() == 0) r = m.invoke(source); // 没有参数的情况.直接调用
            else if (m.getParameterCount() == 1) { // 1个参数的情况
                Class<?> t = m.getParameterTypes()[0];
                if (EC.class.isAssignableFrom(t)) r = m.invoke(source, ec);
                else if (t.isArray()) { // 如果是数组需要转下类型
                    Object arr = Array.newInstance(t.getComponentType(), args(ec).length);
                    for (int i = 0; i < args(ec).length; i++) {
                        Array.set(arr, i, t.getComponentType().cast(args(ec)[i]));
                    }
                    r = m.invoke(source, arr);
                }
                else {
                    Object arg = (args(ec) == null || args(ec).length < 1) ? null : args(ec)[0];
                    r = m.invoke(source, arg);
                }
            } else { // 参数个数多于1个的情况
                Object[] args = new Object[m.getParameterCount()]; // 参数传少了, 补null, 传多了忽略
                if (EC.class.isAssignableFrom(m.getParameterTypes()[0])) {
                    args[0] = ec;
                    if (args(ec) != null) {
                        for (int i = 1; i <= args(ec).length && i < m.getParameterCount(); i++) args[i] = args(ec)[i-1];
                    }
                } else if (args(ec) != null) {
                    for (int i = 0; i < args(ec).length && i < m.getParameterCount(); i++) args[i] = args(ec)[i];
                }
                r = m.invoke(source, args);
            }
            resultAcceptor.accept(r, null);
        }

        @Override
        public String execId() { return source.getClass().getName() + "#" + m.getName(); }
    }


    public static class RunnableListener extends Listener {
        protected final Runnable fn;

        public RunnableListener(Runnable fn) {
            if (fn == null) throw new IllegalArgumentException("RunnableListener fn required");
            this.fn = fn;
        }

        @Override
        protected void doInvoke(EC ec, BiConsumer<Object, Throwable> resultAcceptor) {
            fn.run();
            resultAcceptor.accept(null, null);
        }

        @Override
        public String execId() { return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); }
    }


    public static class FunctionListener<T, R> extends Listener {
        protected final Function<T, R> fn;

        public FunctionListener(Function<T, R> fn) {
            if (fn == null) throw new IllegalArgumentException("FunctionListener fn required");
            this.fn = fn;
        }

        @Override
        protected void doInvoke(EC ec, BiConsumer<Object, Throwable> resultAcceptor) {
            resultAcceptor.accept(fn.apply(args(ec) != null && args(ec).length > 0 ? (T) args(ec)[0] : null), null);
        }

        @Override
        public String execId() { return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); }
    }


    public static class BiFunctionListener<T, U, R> extends Listener {
        protected final BiFunction<T, U, R> fn;

        public BiFunctionListener(BiFunction<T, U, R> fn) {
            if (fn == null) throw new IllegalArgumentException("BiFunctionListener fn required");
            this.fn = fn;
        }

        @Override
        protected void doInvoke(EC ec, BiConsumer<Object, Throwable> resultAcceptor) {
            resultAcceptor.accept(fn.apply(
                    args(ec) != null && args(ec).length > 0 ? (T) args(ec)[0] : null,
                    args(ec) != null && args(ec).length > 1 ? (U) args(ec)[1] : null
            ), null);
        }

        @Override
        public String execId() { return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); }
    }

}
